package xu.jiang.report.dealer.report;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import xu.jiang.report.Param;
import xu.jiang.report.R;
import xu.jiang.report.bean.RangeCell;
import xu.jiang.report.dealer.layer.*;
import xu.jiang.report.util.BooleanMsg;
import xu.jiang.report.util.report.MergingCellUtil;
import xu.jiang.report.util.report.POIUtil;
import xu.jiang.report.util.report.RegexUtil;

import java.util.*;

/**
 * 参数解析器 识别模板参数和提取分层的功能 统一遍历一次主模板，找到分页参数，且如果存在层，将层提取出来
 * 并将结果 写回 param.layers
 *
 * @see
 * @author jx
 * @date 2017年5月5日 下午3:20:38
 */
public  class ParamAnalyzer {
    private final Param param;
    /**
     * 存储分页属性的同胞属性
     */
    // public final Set<String> pagingBrothersColumns = new
    // HashSet<String>();
    /**
     * 简单的模板解析结构 内容：记录所有的层（层行号,层内值的集合），全部存储，先不区分是组还是单层(循环层或汇总层)
     */
    private TreeMap<Integer, ILayerDealer> simpleLayers = new TreeMap<>();

    /**
     * simpleLayers经过了组过滤后的数据
     */
    private TreeMap<Integer, AbstractIndexLayer> filteredLayers = new TreeMap<Integer, AbstractIndexLayer>();
    /**
     * 范围形式的Report级别参数
     */
    private RangeParam rangeParamAnalyzer = null;

    /**
     * 任务有2个： 1 找到分页属性的同胞属性名称 2 找到所有的层或组
     */
    public ParamAnalyzer(Param param) throws Exception {

        this.param = param;

        this.rangeParamAnalyzer = new RangeParam(this.param);

        this.initLayers();

        this.cacheLayers();

    }

    private ParamAnalyzer initLayers() throws Exception {

        // 遍历整个主模板Sheet
        int lastR = this.param.mainSheet.getLastRowNum();

        for (int i = 0; i <= lastR; i++) {// 遍历行

            Row r = this.param.mainSheet.getRow(i);
            if (r == null) {// blank row ,pass it !!
                continue;
            }

            // 存储层的属性
            List<String> columns = new LinkedList<>();
            // 临时存储合并单元格,不重复
            Set<CellRangeAddress> mergingColumns = new HashSet<>();

            //是否是层
            boolean isLayer = false;

            //是否存在组内层
//				boolean isGroup = false;

            for (int j = 0; j < r.getLastCellNum(); j++) {// 遍历单元格

                Cell c = POIUtil.getOrCreateCell(r, j);// cell肯定不是null
                String cellValue = POIUtil.getCellValue(c);// cellValue肯定不是null
                /*
                 * 将层放入这一层的列中
                 *
                 */
                columns.add(cellValue);

                // 保存合并单元格
                BooleanMsg<RangeCell> status = MergingCellUtil.isMergingCell(c);
                if (status.isOk()) {
                    if (!MergingCellUtil.exists(mergingColumns, status.otherObject.range)) {
                        mergingColumns.add(status.otherObject.range);

                    }
                }

                if (StringUtils.isBlank(cellValue)) {
                    continue;
                }
                cellValue = cellValue.trim();

                // 是否为分页属性的同胞属性
                if (cellValue.matches(".*" + R.SHEET_REGEX + ".*")) {

                    this.param.pagingColumnContainer.add(cellValue);

                }

//					// 是否为组内层
//					if (cellValue.matches(R.Report.GROUP_REGEX_COMPLETE)) {
//						isGroup = true;
//
//					}

                if (cellValue.matches(R.Report.RANGE_REGEX)) {

                    rangeParamAnalyzer.work(c);

                }

                // 是否为普通替换符（可能是循环层或统计层）
                if (cellValue.matches(".*" + R.OBJECT_PROPERTY_REGEX + ".*")) {
                    /*
                     * 单元格的内容全部存储起来，显然这里可能包含其他替换符和运算符
                     */
                    isLayer = true;
                }

            }
            /*
             * 只将是层的属性封装为层结构
             */
            if (!isLayer) {
                continue;
            }
            // 将该层的属性的值添加到Layers中

            ILayerDealer layer = LayerFactory.instance.create(new LayerBaseParam(i, columns, mergingColumns, this.param));

            this.simpleLayers.put(i, layer);

        }

        return this;

    }
    private ParamAnalyzer cacheLayers(){
        // 都是单独层
        for (ILayerDealer layer : this.simpleLayers.values()) {
            this.param.layers.put(layer.getIndex(), layer);
        }

        return this;
    }

    /**
     * 范围形式的Report级别参数
     *
     * @see
     * @author jx
     * @date 2017年5月17日 上午9:33:50
     */
    private class RangeParam {

        private final Param param;

        public RangeParam(Param param) {
            this.param = param;
        }

        /**
         * 核心处理逻辑
         *
         //			 * @param mainValue
         *            去掉范围后的真正实体串， 即{xxx}[1~9]-->{xxx}
         *            或{xxx}(1~9)[3,9,8]-->{xxx}
         //			 * @param pushMerging
         *            小括号的部分,形如(0~9)或(2,9,8)
         //			 * @param pushCoping
         *            中括号的部分,形如[0~9]或[2,9,8]
         * @throws Exception
         */
        public void work(Cell c) throws Exception {

            // Sheet s = c.getSheet();
            // Row r = c.getRow();
            // 获取值
            String cellValue = POIUtil.getCellValue(c);

            String mainValue = cellValue.replaceAll(R.Report.RANGE_PIECE_PUSH_COPYING, "")
                    .replaceAll(R.Report.RANGE_PIECE_PUSH_MERGING, "");

            String range = cellValue.replace(mainValue, "");

            // 平移复制 的数字范围
            List<Integer> nums_pushCopying = RegexUtil
                    .analyzeIntegers(range.replaceAll(R.Report.RANGE_PIECE_PUSH_MERGING, ""));
            // 平移合并的数字范围
            List<Integer> nums_pushMerging = RegexUtil
                    .analyzeIntegers(range.replaceAll(R.Report.RANGE_PIECE_PUSH_COPYING, ""));

            if (mainValue.matches(R.Report.UI_WELDED_DATE_RANGE_PIECE)) {

                // this.replaceDynamicWeldedDate(c);

                String weldedDate_start = this.param.uiParams.get("ui." + R.SRING_START_DATE);
                String weldedDate_end = this.param.uiParams.get("ui." + R.SRING_END_DATE);

                List<String> dates = AbstractReportDealer.getRangeDate(weldedDate_start, weldedDate_end);

                // 先处理pushMerging,因为替换符本身占据一个cell，故需要新增的blankCell应该减一
                putBlankCellAndMerge4Rows(c, nums_pushMerging, dates.size() - 1);
                // 平移并插入本行
                POIUtil.insertCellAndPutValue(c, dates);
                // 再处理pushCopying
                replaceDynamicValue4RowsByRegex(c, nums_pushCopying, R.SRING_DYNAMIC_CURRENT_DATE, dates, true);
            }
        }

        /**
         * 在c位置右边新增length_newBlankCell个空白单元格，然后与c组成合并单元格 如果c已经是合并单元格，就将它扩展
         *
         * @param c
         *            当前单元格
         * @param arr_RowIndex
         *            是数字数组，里面存储行号，代表需要追加空白格且合并延伸的行
         * @param length_newBlankCell
         *            新增的空白单元格
         */
        private  void putBlankCellAndMerge4Rows(Cell c, List<Integer> arr_RowIndex, int length_newBlankCell) {
            if (c == null || arr_RowIndex == null || length_newBlankCell <= 0 || arr_RowIndex.size() <= 0) {
                return;
            }
            // 当前单元格的列号
            int j = c.getColumnIndex();
            for (int a : arr_RowIndex) {

                // 处理每一行的延伸
                int firstRow = a - 1;// 行号要减1
                int firstCol = j;
                if (firstRow == c.getRowIndex() && firstCol == c.getColumnIndex()) {
                    continue;// 提出本行的c,不处理c
                }
                Cell first_relationCell = POIUtil.getOrCreateCell(c.getSheet(), firstRow, firstCol);
                MergingCellUtil.insertBlankAndMergeCell2Cell(first_relationCell, length_newBlankCell);

            }

        }

        /**
         * 遍历每一行，替换各个列中的每个替换符Regex的值
         *
         * 如果c是合并单元格，则可能c的colspan>1,即同列的不同行的单元格包含多个单元格
         * 故在平移复制时，要保证colspan内的单元格都平移复制，且对regex进行替换
         *
         * @param c
         *            ，其跨列代表平移范围
         * @param arr_RowIndex 行号数组，代表平移涉及的行
         * @param regex
         *            替换正则
         * @param dynamicValues
         *            值的数组，数组大小代表平移次数 横向的值的数组，每个值对应一个列,其中包含要通过Regex替换的值,
         * @param isValue4Sql
         *            dynamicValues值是sql中的值，如果是加''
         */
        private   void replaceDynamicValue4RowsByRegex(Cell c, List<Integer> arr_RowIndex, String regex,
                                                           List<? extends Object> dynamicValues, boolean isValue4Sql) {
            if (c == null || arr_RowIndex == null || dynamicValues == null || dynamicValues.size() <= 0
                    || arr_RowIndex.size() <= 0) {
                return;
            }
            int colStepByGroup = 1;// 默认c只覆盖一列
            Sheet s = c.getSheet();
            BooleanMsg<RangeCell> state = MergingCellUtil.isMergingCell(c);
            if (state.isOk()) {
                colStepByGroup = state.otherObject.getColSpan();
            }

            /*
             * 遍历数据，每个数据是一次平移，会生成一列， 单元格内包含替换符，数据是需要替换的值，
             * 因为colStepByGroup覆盖的单元格也要放数据的，故需要少移动一次
             *
             * 这里数据采用倒着替换的方式，随着向后推移，就变成正序了
             */
            for (int i = dynamicValues.size() - 1; i >= 0; i--) {

                if (i == 0) {// 无需平移了

                    String last_replaceV = dynamicValues.get(0) == null ? "" : dynamicValues.get(0).toString();

                    // 循环每一行，最后一个数值无需平移，
                    for (int index = 0; index < arr_RowIndex.size(); index++) {

                        int nextRowIndex = arr_RowIndex.get(index) - 1;// 行号要减一

                        if (nextRowIndex == c.getRowIndex()) {// 和c相同额行不处理
                            continue;
                        }
                        for (int j = 0; j < colStepByGroup; j++) {

                            Cell downCell = POIUtil.getOrCreateCell(s, nextRowIndex, c.getColumnIndex() + j);
                            String cellValue = POIUtil.getCellValue(downCell);
                            String newReplaceV = "";
                            if (isValue4Sql) {// 是值，直接替换正则
                                newReplaceV = "'" + last_replaceV + "'";
                            } else {
                                // 是普通属性,要拼凑成对象属性格式
                                // 先从单元格中获取对象
                                String objectName = RegexUtil.getObjectName4Value(cellValue);
                                newReplaceV = "{" + objectName + "." + last_replaceV + "}";
                            }
                            downCell.setCellValue(cellValue.replaceAll(regex, newReplaceV));

                        }
                    }

                } else {
                    String replaceV = dynamicValues.get(i) == null ? "" : dynamicValues.get(i).toString();

                    // 循环每一行，最后一个数值无需平移，
                    for (int index = 0; index < arr_RowIndex.size(); index++) {

                        int nextRowIndex = arr_RowIndex.get(index) - 1;// 行号要减一

                        if (nextRowIndex == c.getRowIndex()) {// 和c相同额行不处理
                            continue;
                        }
                        Cell first = POIUtil.getOrCreateCell(s, nextRowIndex, c.getColumnIndex());
                        // 先平移 必须从第一个单元格开始平移复制即可
                        POIUtil.insertAndCopyValue(first, 1, colStepByGroup);
                        /*
                         * 每一次平移，步调为colStepByGroup，必须保证colStepByGroup
                         * 范围覆盖的每个一个单元格都是相同的数据
                         */
                        for (int j = 0; j < colStepByGroup; j++) {
                            // 总是去修改下一个位置的值
                            Cell downCell = POIUtil.getOrCreateCell(s, nextRowIndex,
                                    c.getColumnIndex() + j + colStepByGroup);
                            String cellValue = POIUtil.getCellValue(downCell);
                            String newReplaceV = "";
                            if (isValue4Sql) {// 是值，直接替换正则
                                newReplaceV = "'" + replaceV + "'";
                            } else {
                                // 是普通属性,要拼凑成对象属性格式
                                // 先从单元格中获取对象
                                String objectName = RegexUtil.getObjectName4Value(cellValue);
                                newReplaceV = "{" + objectName + "." + replaceV + "}";

                            }
                            downCell.setCellValue(cellValue.replaceAll(regex, newReplaceV));
                        }

                    }
                }

            }
        }



    }



}

